import {getCurrentInstance} from 'vue'
import {kebabCase} from "../utils/KebabCase";
import {createPlainEvent} from "../plugins/Event";
import {SimpleFunction} from "../shims";

type EventListener<EmitsValue> = EmitsValue extends (...args: any[]) => any ? Parameters<EmitsValue> : never

export type ComponentEvent<Emit> = {
  emit: { [key in keyof Emit]: (...args: EventListener<Emit[key]>) => void }, // 这里实现了, 只要你不在emits选项里声明你要触发的事件, 你在组件里就.(点)不出来该事件(强点 ts会报错)
  on: { [key in keyof Emit]: (cb: (...args: EventListener<Emit[key]>) => void) => void },
  once: { [key in keyof Emit]: (cb: (...args: EventListener<Emit[key]>) => void) => void },
  off: { [key in keyof Emit]: (cb: (...args: EventListener<Emit[key]>) => void) => void },
}

// 非v-model的vue原生事件, 以及我们PlainEvent(我们这里自己实现的)事件发布系统上注册的事件
// focus                -> focus
// itemClick            -> item-click
//
// v-model:
// updateModelValue     -> update:modelValue
// updateStart          -> update:start
function emitName2ListenName(emitName: string): string {
  const match = emitName.match(/update([A-Z])(.*)/)
  if (match) {
    return `update:${match[1].toLowerCase()}${match[2]}`
  }
  return kebabCase(emitName)!
}

export function getComponentEmit<T>(emitObject: T): T {
  return {
    change: null, //todo
    ...Object.keys(emitObject || {}).reduce((ret: any, key: string) => {
      ret[emitName2ListenName(key)] = (emitObject as any)[key] //@myEvent 将会变成 @myevent,So这会导致 myEvent 不可能被监听到。 因此，我们推荐你始终使用 kebab-case 的事件名。
      return ret
    }, {} as any),
  }
}

export function useEvent<T>(emitObject: T): ComponentEvent<T> {

  const ctx = getCurrentInstance()! //←这个getCurrentInstance获取到的ctx是当前组件的ctx
  const event = createPlainEvent() //闭包暂留, 每次createPlainEvent运行都会创建一个新的存储事件的地

  ///闭包暂留
  const emit = {} as any;
  const on = {} as any;
  const once = {} as any;
  const off = {} as any;


  Object.keys(emitObject || {}).forEach(key => {
    /*派发事件名称，横杠命名*/
    const kebabCaseName = emitName2ListenName(key)

    emit[key] = (...args: any[]) => {
      ctx.emit(kebabCaseName, ...args) // v-model、@click、 ... // 通知vue的事件系统
      event.emit(kebabCaseName, ...args) // 单纯的发布订阅(每个组件实例都会有个PlainEvent(我们这里自己实现的)) //通知我们自己的PlainEvent事件系统
      if (key === 'updateModelValue') { //todo
        ctx.emit('change', ...args)
        event.emit('change', ...args)
      }
    }
    on[key] = (fn: SimpleFunction) => event.on(kebabCaseName, fn)
    once[key] = (fn: SimpleFunction) => event.once(kebabCaseName, fn)
    off[key] = (fn: SimpleFunction) => event.off(kebabCaseName, fn)
  })

  return {
    emit, on, once, off
  } as any
}
